//	======================================================================
//	File:    Flv_Table.cxx - Flv_Table implementation
//	Program: Flv_Table - FLTK Table Widget
//	Version: 0.1.0
//	Started: 11/21/99
//
//	Copyright (C) 1999 Laurence Charlton
//
//	Description:
//	Flv_Table implements a table/grid.  No data is stored
//	in the widget.  Supports headers/footers for rows and columns,
//	natively supports a single row height and column width per table.
//	Row and column grids can be turned on and off.  Supports no scroll
//	bars as well as horizontal/vertical automatic or always on scroll bars.
//	Also support cell selection and row selection modes.  In row selection
//	mode it acts like a pumped-up list widget.
//	Uses absolute cell references.
//
//	row -1 is defined as the row header
//	row -2 is defined as the row footer
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA.
//	======================================================================

#include <FL/Flv_Table.H>
#include <FL/Enumerations.H>
#include <FL/fl_draw.H>
#include <stdio.h>

#define DOcb(x) ((callback_when() & (x))==(x))

//	Resizing constants
#define FUDGE 2
#define MOVE_X 1
#define MOVE_Y 2
#define MOVE_XY (MOVE_X|MOVE_Y)

#ifndef max
#define max(a,b) ((a)>(b)?(a):(b))
#endif
#ifndef min
#define min(a,b) ((a)<(b)?(a):(b))
#endif

static Fl_Cursor last_cursor = FL_CURSOR_DEFAULT;
static int drag_col=-4, drag_row=-4, anchor_left, anchor_top;


Flv_Table::Flv_Table( int X, int Y, int W, int H, const char *l ) :
    Flv_List(X,Y,W,H,l)
{
    edit_col = -1;
    vcol = 0;
    vcols = 0;
    vcol_width = 40;
    vmove_on_enter = FLV_MOVE_ON_ENTER_COL_ROW;
    vselect_col = 0;
    vbuttons = FLV_BUTTON1 | FLV_BUTTON2 | FLV_BUTTON3;
}

Flv_Table::~Flv_Table()
{
}

//	Probbably won't need to over-ride this for future table widgets
void Flv_Table::draw_row( int Offset, int &X, int &Y, int &, int &H, int R )
{
    int c, cw, CX, FW;
    int dX, dY, dW, dH;
    int TX, TY, TW, TH;
    Flv_Style s;

    //	Calculate clipping height
    client_area(dX,dY,dW,dH);

    FW = (col_footer()?col_width(-2):0);

    CX = X;

    //	Draw column header
    if (col_header())
    {
        cw = col_width(-1);		//	Column width
        TX = CX;
        TY = Y;
        TW = cw;
        TH = H;
        draw_cell( 0, TX, TY, TW, TH, R, -1 );
        CX += cw;
        dX += cw;
        dW -= cw;
    }

    dW -= FW;
    //	Draw column footer
    if (FW)
    {
        TX = dX+dW;
        TY = Y;
        TW = FW;
        TH = H;
        draw_cell( 0, TX, TY, TW, TH, R, -2 );
    }

    fl_clip( dX, Y, dW, H );			//	Clip data area
    for (c=0;	c<vcols && CX-Offset<dX+dW;	c++, CX+=cw )
    {
        cw = col_width(c);					//	Column width
        if (CX-Offset+cw<dX)				//	Before left
            continue;
        fl_clip( CX-Offset, Y, cw, H );
        TX = CX;
        TY = Y;
        TW = cw;
        TH = H;
        draw_cell( Offset, TX, TY, TW, TH, R, c );
        fl_pop_clip();
    }
    //	If we're selecting a row, put the box around it.
    if (R==row() && select_row() )
    {
        fl_color( fl_contrast(FL_BLACK, selection_color()) );
        fl_rect( dX, Y, dW, H );
    }
    //	Fill-in area at right of list
    if (CX-Offset<dX+dW)
    {
        cw = dX+dW-(CX-Offset);
        fl_color( dead_space_color() );
        fl_rectf( CX-Offset, Y, cw, H );
    }
    fl_pop_clip();
}

//	You will certainly want to override this
void Flv_Table::draw_cell( int Offset, int &X, int &Y, int &W, int &H, int R, int C )
{
    Fl_Boxtype bt;
    Flv_Style s;

    X -= Offset;

    get_style(s, R, C);
    if (Fl::focus()==this || persist_select())
        add_selection_style(s, R, C);

    if (row_divider())
        s.border( s.border()|FLVB_BOTTOM );
    if (col_divider())
        s.border( s.border()|FLVB_RIGHT );

    draw_border( s, X, Y, W, H );
    bt = s.frame();

    fl_color( s.background() );
    fl_rectf(X,Y,W,H );
#ifdef FLTK_2
    bt->draw(X,Y,W,H,s.background());
    //	Normally you would use the next line to get the client area to draw
    bt->inset( X, Y, W, H );
#else
    draw_box( bt, X, Y, W, H, s.background() );
    //	Normally you would use the next lines to get the client area to draw
    X+= (Fl::box_dx(bt));
    Y+= (Fl::box_dy(bt));
    W-= (Fl::box_dw(bt));
    H-= (Fl::box_dh(bt));
#endif
    //	Drawing selection rectangle for cell
    if (R>-1 && C>-1 && R==row() && C==col() && !select_row() &&
            (Fl::focus()==this || persist_select()))
    {
        fl_color( fl_contrast(text_color(), selection_color()) );
        fl_rect( X, Y, W, H);
    }

    X+=s.x_margin();
    Y+=s.y_margin();
    W-=s.x_margin()*2;
    H-=s.y_margin()*2;
    X += Offset;
    //	Get set-up to draw text
    fl_font( s.font(), s.font_size() );
    if (!active())
        s.foreground( fl_inactive(s.foreground()) );
    fl_color(s.foreground());
}

bool Flv_Table::get_cell_bounds( int &X, int &Y, int &W, int &H, int R, int C )
{
    int x, y, w, h, r, rh, B, cx;

    X = Y = W = H = 0;
    cell_area(x,y,w,h);
    B = y+h;

    for (r=top_row();	r<rows() && r<R;	y += rh, r++ )
    {
        rh = row_height(r);
        if (y>B)
            break;
    }
    if (r!=R)
        return false;
    Y = y;
    H = row_height(R);
    if (Y+H>B)
        H = B-Y;

    cx = x - row_offset();
    for (r=0;	r<cols() && r<C;	cx += rh, r++ )
    {
        rh = col_width(r);
        if (cx>x+w)
            break;
    }
    rh = col_width(r);
    if (r!=C || cx+rh<x)
    {
        X = Y = W = H = 0;
        return false;
    }

    X = cx;
    if (X<x)
    {
        rh -= (x-X);
        X = x;
    }
    if (X+rh>x+w)
        rh = (x+w)-X;
    if (rh>w)
        rh = w;
    if (rh<0)
        rh = 0;
    W = rh;
    return true;
}

void Flv_Table::draw(void)
{
    int r, rh, rw;
    int X, Y, W, H, B, FW;
    int CX, CY, CW, CH;
    Flv_Style s;
    int t, c;
    //char buf[30];

    //	Initially verify we aren't on a locked cell
    r = row();
    c = col();
    while(!select_locked())
    {
        get_style(s,r,c);
        if (!s.locked())
        {
            row(r);
            col(c);
            break;
        }
        c++;
        if (c==cols())
        {
            c = 0;
            r++;
            if (r==rows())
                break;
        }
    }

    //	Make sure we have an editor if editing!
//	if (!veditor && vediting)
//		switch_editor(row(),col());

    //	We need to know what the row width will be
    //	so we'll calculate that and then let normal drawing
    //	take over.
    if (!feature_test(FLVF_MULTI_SELECT))
        select_start_col(vcol);

    for (c=cols(), rw=t=0;	t<c;	t++ )
        rw += col_width(t);
    if (col_header())
        rw += col_width(-1);
    if (col_footer())
        rw += col_width(-2);

    row_width(rw);					//	Set the row width so we can draw intelligently

    start_draw(X,Y,W,H,rw);

    //	This is why draw is here and we're not using the code from
    //	Flv_List... It sucks, but I really didn't like the flickering
    //	from the column footers getting erased and then redrawn...
    FW = (col_footer()?col_width(-2):0);

    B = W-(rw-row_offset())-FW;
    //	Fill-in area at right of list
    if (B>0)
    {
        fl_color( dead_space_color() );
        fl_rectf( X+rw-row_offset(), Y, B, H );
    }

    B = Y + H;
    fl_clip( X, Y, W, H );
    //	Draw rows
    for (	r=top_row();	Y<B && r<rows();	r++, Y+=rh )
    {
        rh = row_height(r);
        if ( vlast_row==row() || (vlast_row!=row() && (r==vlast_row || r==row())) )
        {
            fl_clip( X, Y, W, rh);
            CX=X;
            CY=Y;
            CW=rw;
            CH=rh;
            draw_row( row_offset(), CX, CY, CW, CH, r );
            fl_pop_clip();
        }
    }
    vlast_row = row();

    //	Fill-in area at bottom of list
    if (Y<B)
    {
        fl_color( dead_space_color() );
        fl_rectf( X, Y, W, B-Y );
    }
    fl_pop_clip();
}

void Flv_Table::add_selection_style( Flv_Style &s, int R, int C )
{
    if (!multi_select())		//	If not multi row selection
    {
        select_start_row( row() );
        select_start_col( col() );
    }
    if (R>-1 && C>-1)
        if ( (select_row() && row_selected(R)) ||
                (!select_row() && cell_selected(R,C)) )
        {
            if(Fl::focus() != this)
            {
                s.background( fl_color_average(selection_color(), FL_WHITE, 0.4) );
                s.foreground( text_color());
            }
            else
            {
                s.background( selection_color() );
                s.foreground( selection_text_color());
            }
        }
}

void Flv_Table::cell_area(int &X, int &Y, int &W, int &H )
{
    client_area(X,Y,W,H);
    if (label() && *label())
    {
        Y += row_height(FLV_TITLE);
        H -= row_height(FLV_TITLE);
    }

    if (row_header())
    {
        Y += row_height(FLV_ROW_HEADER);
        H -= row_height(FLV_ROW_HEADER);
    }
    if (row_footer())
    {
        H -= row_height(FLV_ROW_FOOTER);
    }
    if (col_header())
    {
        X += col_width(FLV_COL_HEADER);
        W -= col_width(FLV_COL_HEADER);
    }
    if (col_footer())
    {
        W -= col_width(FLV_COL_FOOTER);
    }
}

bool Flv_Table::cell_selected(int R, int C)
{
    return (col_selected(C) && row_selected(R));
}

int Flv_Table::col_width(int C)					//	Get column width
{
    int fw = vcol_width;
    Flv_Style *cols;

    if (global_style.width_defined())
        fw = global_style.width();
    cols = col_style.find(C);
    if (cols)
        if (cols->width_defined())
            fw = cols->width();
    return fw;
}

int Flv_Table::col_width(int n, int c)	//	Set column width
{
    int cw = col_width(c);

    if (c<-3)	c=-3;
    if (c>=vcols) c=vcols-1;
    if (n<0) n=0;
    if (n!=cw)
    {
        col_style[c].width(n);
        damage(FL_DAMAGE_CHILD);
    }
    return col_width(c);
}

void Flv_Table::get_style( Flv_Style &s, int R, int C )
{
    Flv_Style *rows, *cols, *cells;

    Flv_List::get_style( s, R );
    rows = row_style.skip_to(R);

    if (R!=-3)
    {
        cols = col_style.skip_to(C);
        if (cols) s = *cols;
    }

    if (C<0 || R<0)							//	Headers/Labels have different default
    {
        //	Note: we can still override at cell level
        if (parent())
            s.background( parent()->color() );
        else
            s.background( FL_WHITE );
        s.frame(FL_THIN_UP_BOX);
        s.border( FLVB_NONE );
        s.border_spacing(0);
    }

    cells = (rows?rows->cell_style.skip_to(C):NULL);
    if (cells)	s = *cells;
}

int Flv_Table::handle(int event)
{
    int stat=0, x, y, X,Y,W,H, r, c;
    Flv_Style s;

    switch(event)
    {
    case FL_RELEASE:
    case FL_DRAG:
        if (!vediting || !veditor)
            break;

    case FL_PUSH:
        if (Fl::event_button1()==0)
            break;
        if (drag_row!=-4 || drag_col!=-4)
            break;
        x = Fl::event_x();
        y = Fl::event_y();
        if (!vediting)
        {
            if (edit_when()==FLV_EDIT_MANUAL)
                break;
            r = row();
            c = col();
            if (r<0 || c<0)
                break;
            cell_area(X,Y,W,H);
            stat = internal_handle(event);
            if (stat && r==row() && c==col() && x>=X && x<X+W && y>=Y && y<Y+H )
            {
                start_edit();
                return 1;
            }
            return 0;
        }

        //	If these are occur outside the editor, we don't want the
        //	child widget processing them
        if (x<veditor->x() || y<veditor->y() || x>veditor->x()+veditor->w() ||
                y>veditor->y()+veditor->h())
            break;
        stat = veditor->handle(event);
        if (stat)
        {
            veditor->draw();
            return 1;
        }
        break;
    }

    if (event==FL_SHORTCUT && vediting)
    {
        if (Fl::event_key()==FL_Enter)
        {
            end_edit();													//	Save editor/ quit editing
            Fl::focus(this);
//			take_focus();
            internal_handle(FL_KEYBOARD);
            damage(FL_DAMAGE_CHILD);
            return 1;
        }
        switch( Fl::event_key() )
        {
        case FL_Shift_L:
        case FL_Shift_R:
        case FL_Control_L:
        case FL_Control_R:
        case FL_Meta_L:
        case FL_Meta_R:
        case FL_Alt_L:
        case FL_Alt_R:
            break;
        default:
            stat = internal_handle(FL_KEYBOARD);
        }
    }
    else
    {
        stat = internal_handle(event);
        if (!stat)
        {
            //	Jump start editing if automatic
            if (event==FL_KEYBOARD && !vediting && edit_when()==FLV_EDIT_AUTOMATIC )
            {
                switch( Fl::event_key() )
                {
                case FL_Shift_L:
                case FL_Shift_R:
                case FL_Control_L:
                case FL_Control_R:
                case FL_Meta_L:
                case FL_Meta_R:
                case FL_Alt_L:
                case FL_Alt_R:
                    break;
                default:
                    start_edit();
                    if (veditor)
                    {
                        stat = veditor->handle(event);
                        if (stat)
                        {
                            Fl::focus(veditor);
//								veditor->take_focus();
                            veditor->draw();
                            return 1;
                        }
                    }
                    cancel_edit();
                }
            }
        }
    }

    if (veditor && Fl::focus()==this)
    {
        Fl::focus(veditor);
//		veditor->take_focus();
        veditor->handle(FL_FOCUS);
    }
    if (stat && veditor)
        veditor->draw();
    return stat;
}

static int _last_click_y = 0;
static int _scroll_direction = 0;
static int _timeout_count = 0;

static void _scrollerCB(void *ud)
{
	if(_timeout_count){
		_timeout_count--;
		Flv_Table *table = (Flv_Table*)ud;
		Fl::repeat_timeout( 0.05, _scrollerCB, table );
		//redraw();
		Fl::e_keysym = _scroll_direction < 0 ? FL_Up : FL_Down;
		table->handle(FL_KEYBOARD);
	}
}

int Flv_Table::internal_handle(int event)
{
    int TX, TY, r, c, cd, rd;
    Flv_Style s;
    static int LX, LY;


    switch( event )
    {
    case FL_KEYBOARD:
        break;

    case FL_ENTER:
    case FL_LEAVE:
        vclicks = 0;
        return Fl_Group::handle(event);

    case FL_FOCUS:
        if(rows() == 0) return 0;
    case FL_UNFOCUS:
        if(rows() > 0) redraw();
        return 1;

    case FL_MOVE:
        TY = Fl::event_y();
        TX = Fl::event_x();
        if ( LX-TX<-3 || LX-TX>3 || LY-TY<-3 || LY-TY>3 )
        {
            LX = TX;
            LY = TY;
            vclicks = 0;
        }
        check_cursor();
        return Fl_Group::handle(event);

    case FL_RELEASE:
		if(is_dragging){
			is_dragging = false;
/*
			int time_span = Fl::getMilliSpan(last_click_timestamp);
			if(time_span <= 50){
				_scroll_direction = Fl::event_y() - _last_click_y;
				_timeout_count = (50-time_span)/2;
				Fl::add_timeout( 0.1, _scrollerCB, this );
			}
*/
		}
        drag_row = drag_col = -4;
        Fl_Group::handle(event);
        return 1;

    case FL_DRAG:
        vclicks=0;
		is_dragging = true;
        if (check_resize())
            return 1;
    case FL_PUSH:
		last_click_timestamp = Fl::getMilliCount();
		_timeout_count = 0;
		if(!is_dragging)_last_click_y = Fl::event_y();
        //	Dragging not clicking
        if (drag_row!=-4 || drag_col != -4)
            return 1;
        r = 0;
        if (Fl::event_button1() && (buttons() & FLV_BUTTON1)) r=1;
        if (Fl::event_button2() && (buttons() & FLV_BUTTON2)) r=1;
        if (Fl::event_button3() && (buttons() & FLV_BUTTON3)) r=1;
        if (r==0)
        {
            vclicks = 0;
            return 0;
        }

        //	Determine if col was clicked and highlight it
        TY = Fl::event_y();
        TX = Fl::event_x();
        r = get_row(TX,TY);
        c = get_col(TX,TY);
        //bug that prevents handle click on scrollbars
        //if (r==-4 && c==-4)
        if (r<0 && c<0)
        {
            vclicks = 0;
            return Fl_Group::handle(event);
        }
        if ( LX-TX>-3 && LX-TX<3 && LY-TY>-3 && LY-TY<3)
            vclicks++;
        else
        {
            vclicks=1;
            LX = TX;
            LY = TY;
        }

        damage(FL_DAMAGE_CHILD);
        rd = (r>row()?1:r==row()?0:-1);
        cd = (c>col()?1:c==col()?0:-1);
        if (r>=0)
            row(r);
        if (c>=0)
            col(c);

        if (!multi_select() ||
                (event==FL_PUSH && !Fl::event_state(FL_SHIFT)))
        {
            select_start_row(row());
            select_start_col(col());
        }

        //	At least one header clicked
        if (r<0 || c<0)
        {
            if (r>-4 && c>-4 && r<0 && c<0 && r!=-3)
            {
                if ( DOcb(FLVEcb_ALL_CLICKED) )
                {
                    vwhy_event = FLVE_ALL_CLICKED;
                    do_callback(this, user_data());
                    vwhy_event = 0;
                }
                return 1;
            }

            if ( c>=0 || r==-3 )
            {
                vwhy_event = 0;
                switch( r )
                {
                case -3:
                    if (DOcb(FLVEcb_TITLE_CLICKED))
                        vwhy_event = FLVE_TITLE_CLICKED;
                    break;
                case -2:
                    if (DOcb(FLVEcb_ROW_FOOTER_CLICKED))
                        vwhy_event = FLVE_ROW_FOOTER_CLICKED;
                    break;
                case -1:
                    if (DOcb(FLVEcb_ROW_HEADER_CLICKED))
                        vwhy_event = FLVE_ROW_HEADER_CLICKED;
                    break;
                }
                if (vwhy_event)
                {
                    do_callback(this, user_data());
                    vwhy_event = 0;
                    return 1;
                }
            }
            if ( r>=0 )
            {
                vwhy_event = 0;
                switch( c )
                {
                case -2:
                    if (DOcb(FLVEcb_COL_FOOTER_CLICKED))
                        vwhy_event = FLVE_COL_FOOTER_CLICKED;
                    break;
                case -1:
                    if (DOcb(FLVEcb_COL_HEADER_CLICKED))
                        vwhy_event = FLVE_COL_HEADER_CLICKED;
                    break;
                }
                if (vwhy_event)
                {
                    do_callback(this, user_data());
                    vwhy_event = 0;
                    return 1;
                }
            }
            return 0;
        }


        if (event==FL_PUSH && (rd || cd))
        {
            //	Skip over locked cells
            while(!select_locked())
            {
                get_style(s,r,c);
                if (!s.locked())
                {
                    if (r!=row() || c!=col())
                        vclicks=0;
                    row(r);
                    col(c);
                    break;
                }
                r += rd;
                c += cd;
                if ( r<0 || r>=rows() || c<0 || c>=cols() )
                    break;
            }
        }
        if (event==FL_PUSH)
        {
            if (DOcb(FLVEcb_CLICKED))
            {
                vwhy_event = FLVE_CLICKED;
                do_callback(this, user_data());
                vwhy_event = 0;
            }
            if (vclicks>=vmax_clicks)
                vclicks=0;
        }
        return 1;

    default:
        return Fl_Group::handle(event);
    }

    switch(Fl::event_key())
    {
    case FL_Enter:
        switch( vmove_on_enter)
        {
        case FLV_MOVE_ON_ENTER_ROW_COL:
            if (!move_row(1))
            {
                row(0);
                col(col()+1);
                if (!select_locked())
                {
                    //get_style(s,r,col());
                    get_style(s,r,col());
                    if (!s.locked())
                        move_row(1);
                }
            }
            return 1;
        case FLV_MOVE_ON_ENTER_COL_ROW:
            if (!move_col(1))
            {
                col(0);
                row(row()+1);
                if (!select_locked())
                {
                    get_style(s,r,col());
                    if (!s.locked())
                        move_row(1);
                }
            }
            return 1;
        }
        return 0;

    case FL_Up:
        if (Fl::event_state(FL_CTRL))
            move_row(-row());
        else
            move_row(-1);
        break;

    case FL_Down:
        if (Fl::event_state(FL_CTRL))
            move_row(rows());
        else
            move_row(1);
        break;

    case FL_Page_Down:
        if (Fl::event_state(FL_CTRL))
            move_row( rows() );
        else
            move_row(page_size());
        break;

    case FL_Page_Up:
        if (Fl::event_state(FL_CTRL))
            move_row(-row());
        else
            move_row(-page_size());
        break;

    case FL_Home:
        //	Adjust rows before columns so we redraw everything
        if (Fl::event_state(FL_CTRL))
            move_row(-rows());
        move_col(-cols());
        break;
    case FL_End:
        //	Adjust rows before columns so we redraw everything
        if (Fl::event_state(FL_CTRL))
            move_row(rows());
        move_col(cols());
        break;

    case FL_Right:
        if (select_row())
            return 0;
        if (Fl::event_state(FL_CTRL))
            move_col(cols());
        else
            move_col(1);
        break;

    case FL_Left:
        if (select_row())
            return 0;
        if (Fl::event_state(FL_CTRL))
            move_col(-col());
        else
            move_col(-1);
        break;

    default:
        return Fl_Group::handle(event);
    }

    if (!multi_select() || !Fl::event_state(FL_SHIFT))
    {
        select_start_col(col());
        select_start_row(row());
    }
    return 1;
}

int Flv_Table::row(int n)
{
    int X,Y,W,H;

    if (n>=rows())
        n=rows()-1;
    if (n<0)
        n=0;
    if (n!=vrow)
    {
        vrow = n;
        client_area(X,Y,W,H);
        update_top_row(H);
        end_edit();
        if (edit_when()==FLV_EDIT_ALWAYS)
            switch_editor( row(), col() );
        vlast_row = vrow;
        if (DOcb(FLVEcb_ROW_CHANGED))
        {
            vwhy_event = FLVE_ROW_CHANGED;
            do_callback(this, user_data());
            vwhy_event = 0;
        }
        damage(FL_DAMAGE_CHILD);
    }
    return vrow;
}

int Flv_Table::col( int n )
{
    Flv_Style s;

    if (n>=vcols)
        n=vcols-1;
    if (n<0)
        n=0;
    if (n!=vcol)
    {
        vcol = n;
        end_edit();
        if (edit_when()==FLV_EDIT_ALWAYS)
            switch_editor( row(), col() );

        adjust_for_cell();
        if (DOcb(FLVEcb_COL_CHANGED))
        {
            vwhy_event = FLVE_COL_CHANGED;
            do_callback(this, user_data());
            vwhy_event = 0;
        }
        damage(FL_DAMAGE_CHILD);
    }
    return vcol;
}

bool Flv_Table::col_resizable(int c)								//	Get/set column locked status
{
    Flv_Style *s;
    bool l=true;
    if (global_style.resizable_defined())
        l = global_style.resizable();
    s = col_style.find(c);
    if (s)
        if (s->resizable_defined())
            l = s->resizable();
    return l;
}

bool Flv_Table::col_resizable( bool n, int c)
{
    col_style[c].resizable(n);
    return n;
}

int Flv_Table::cols( int n )
{
    if (n>=0 && n!=vcols)
    {
        vcols = n;
        if (vcol>=vcols)
            col(vcols-1);
        if (vselect_col>vcol)
            select_start_col(vcol);
        update_width();
        if (DOcb(FLVEcb_COLS_CHANGED))
        {
            vwhy_event = FLVE_COLS_CHANGED;
            do_callback(this, user_data());
            vwhy_event = 0;
        }
        damage(FL_DAMAGE_CHILD);
    }
    return vcols;
}

bool Flv_Table::col_selected(int n)
{
    if (vselect_col<vcol)
        return (vselect_col<=n && n<=vcol);
    else
        return (vcol<=n && n<=vselect_col);
}

//	Get column from x,y
int Flv_Table::get_col( int x, int y )
{
    int X, Y, W, H, CX;
    int rw, cw, t, Offset;

    client_area(X,Y,W,H);
    if (label())
    {
        cw = row_height(-3);
        Y+=cw;
        H-=cw;
    }
    if (col_header())
    {
        cw = col_width(-1);
        if (X<=x && x<=X+cw)
            return -1;
        X+=cw;
        W-=cw;
    }
    if (col_footer())
    {
        cw = col_width(-2);
        if (X+W>=x && x>=X+W-cw)
            return -2;
        W -= cw;
    }

    rw = row_width();
    if (!rw)
        rw = W;
    if ( x<X || x>=X+W || y<Y || y>=Y+H || x>X-row_offset()+rw)
        return -4;

    Offset = row_offset();
    for (CX=X, t=0;	t<vcols && CX-Offset<X+W;	t++, CX+=cw )
    {
        cw = col_width(t);
        if (x>=CX-Offset && x<CX-Offset+cw)
            return t;
    }
    return -4;	//	In grey area at bottom?
}

int Flv_Table::select_start_col(int n)
{
    if (n>=vcols)
        n=vcols-1;
    if (n<0)
        n=0;
    if (n!=vselect_col)
    {
        vselect_col = n;
        if (DOcb(FLVEcb_SELECTION_CHANGED))
        {
            vwhy_event = FLVE_SELECTION_CHANGED;
            do_callback(this, user_data());
            vwhy_event = 0;
        }
        damage(FL_DAMAGE_CHILD);
    }
    return vselect_col;
}

void Flv_Table::update_width()
{
    int rw, n;

    for (rw=n=0;	n<vcols;	n++ )
        rw+=(col_width(n));
    if (col_header())
        rw+=col_width(-1);
    if (col_footer())
        rw+=col_width(-2);
    if (rw!=row_width())
    {
        row_width(rw);
        damage(FL_DAMAGE_CHILD);
    }
}

void Flv_Table::adjust_for_cell()
{
    int n, o, cw;
    int X, Y, W, H;

    for (n=o=0;	n<col();	n++ )
        o+=col_width(n);
    if (row_offset()>o)
        row_offset(o);
    else
    {
        client_area(X,Y,W,H);
        if (col_footer())
            W -= col_width(-2);
        if (col_header())
            W -= col_width(-2);
        cw = col_width(col());
        if (o+cw-row_offset()>W)
        {
            row_offset(o+cw-W);
            damage(FL_DAMAGE_CHILD);
        }
    }
}

bool Flv_Table::check_resize(void)
{
    int ex, ey, v;
    int X, Y, W, H;

    if (drag_row<-3 && drag_col<-2)
        return false;

    client_area(X,Y,W,H);
    ex = Fl::event_x();
    ey = Fl::event_y();

    if (drag_row==-3)
    {
        v = ey-anchor_top;
        if (v<2) v=2;
        row_style[drag_row].height(v);
        damage(FL_DAMAGE_CHILD);
        return true;
    }

    if (label() && *label())
    {
        Y += row_height(-3);
        H -= row_height(-3);
    }

    if (drag_col>-3)
    {
        if (drag_col==-2)
        {
            v = anchor_left - ex + col_width(drag_col);
            if (col_header())
            {
                X += col_width(-1);
                W -= col_width(-1);
            }
            if (v>W-1)
            {
                v = W-1;
                anchor_left = X+W-v;
            }
            if (v<2)
            {
                v=2;
                anchor_left = X+W-2;
            }
            col_style[drag_col].width(v);
            damage(FL_DAMAGE_CHILD);
            if (v!=W-1 && v!=2)
                anchor_left = ex;
        }
        else
        {
            v = ex-anchor_left;
            if (drag_col==-1)
            {
                //	Make sure it's in the grid
                if (col_footer())
                    W-=col_width(-2);
                if (v > W-1 )
                    v = W-1;
            }
            if (v<2)
                v=2;
            col_width(v,drag_col);
            damage(FL_DAMAGE_CHILD);
        }
    }

    //	Resize row
    if (drag_row>-4)
    {
        if (drag_row==-2)
        {
            v = anchor_top - ey + row_height(drag_row);
            if (row_header())
            {
                H-=row_height(-1);
                Y+=row_height(-1);
            }
            if (v>H-1)
            {
                v = H-1;
                anchor_top = Y+H-v;
            }
            if (v<2)
            {
                v = 2;
                anchor_top = Y+H-2;
            }
            row_style[drag_row].height(v);
            damage(FL_DAMAGE_CHILD);
            if (v!=2 && v!=H-1)
                anchor_top = ey;
        }
        else
        {
            v = ey-anchor_top;
            if (drag_row==-1)
            {
                if (row_footer())
                    H-=row_height(-2);
                if (v>H-1)
                    v = H-1;
            }
            if (v<2) v=2;
            row_height(v,drag_row);
            damage(FL_DAMAGE_CHILD);
        }
    }
    return true;
}

//	See if we can resize, if so change cursor
void Flv_Table::check_cursor(void)
{
    int X, Y, W, H, R, ey, ex, move=0, WW, size;
    int v;
    bool resize, inh, inv;
    Fl_Cursor cursor;

    //	Assume total miss
    drag_row = drag_col = -4;
    cursor = FL_CURSOR_DEFAULT;

    ex = Fl::event_x();
    ey = Fl::event_y();
    client_area(X,Y,W,H);
    inh = (ex>=X && ex<X+W);
    inv = (ey>=Y && ey<Y+H);

    if (label() && *label())
    {
        size = row_height(-3);
        Y+=size;
        H-=size;
        resize = (ey>=Y-FUDGE && ey<=Y+FUDGE && inh);
        if (resize)
        {
            if (row_resizable(-3))
            {
                drag_row = -3;
                anchor_top = Y-size;
                move |= MOVE_Y;
            }
        }
    }

    //	Trival tests to see if we're in region
    resize = full_resize();
    if (!resize)
    {
        if (row_header())
        {
            size = row_height(-1);
            resize |= (ey>=Y && ey<=Y+size+FUDGE && inh);
        }
        if (!resize)
        {
            if (row_footer())
            {
                size = row_height(-2);
                resize |= (ey<=Y+H && ey>=Y+H-size-FUDGE && inh);
            }
            if (!resize)
            {
                if (col_header())
                {
                    size = col_width(-1);
                    resize |= (ex>=X && ex<=X+size+FUDGE && inh);
                }
                if (!resize)
                {
                    if (col_footer())
                    {
                        size = col_width(-2);
                        resize |= (ex<=X+W && ex>=X+W-size-FUDGE && inh);
                    }
                }
            }
        }
    }

    if (!resize)	//	In general region?
    {
        if (cursor!=last_cursor)
        {
            fl_cursor(cursor,FL_BLACK,FL_WHITE);
            last_cursor = cursor;
        }
        return;
    }

    //	==================================================================
    //	Sweep columns
    WW = X;
    if (col_header())
    {
        size = col_width(-1);
        if (ex>=WW+size-FUDGE && ex<=WW+size+FUDGE && inv)
        {
            if (col_resizable(-1) && (move & MOVE_Y)==0)
            {
                drag_col = -1;
                anchor_left = WW;
                move |= MOVE_X;
            }
        }
        WW += size;
        X += size;
        W -= size;
    }

    if (col_footer())
    {
        size = col_width(-2);
        if (ex>=X+W-size-FUDGE && ex<=X+W-size+FUDGE && inv)
        {
            if (col_resizable(-2))
            {
                drag_col = -2;
                anchor_left = ex;
                move |= MOVE_X;
            }
        }
        W -= size;
    }

    if ( (move & MOVE_X)==0 )
    {
        R = X-row_offset()+row_width()+FUDGE;		//	Right edge of row
        for (WW-=row_offset(), v=0;	WW<R && WW<X+W && v<cols();	WW+=size, v++ )
        {
            size = col_width(v);
            if (WW+size<X)		//	Off left
                continue;
            if (ex>=WW+size-FUDGE && ex<=WW+size+FUDGE && inv)
            {
                if (col_resizable(v))
                {
                    drag_col = v;
                    anchor_left = WW;
                    move |= MOVE_X;		//	Moving col
                }
                break;
            }
        }
    }
    if (col_header())
    {
        X-=col_width(-1);
        W+=col_width(-1);
    }
    if (col_footer())
        W+=col_width(-2);

    //	==================================================================
    //	Sweep rows
    WW = Y;
    if (row_header())
    {
        size = row_height(-1);
        if (ey>=WW+size-FUDGE && ey<=WW+size+FUDGE && inh)
        {
            if (row_resizable(-1))
            {
                drag_row = -1;
                anchor_top = WW;
                move |= MOVE_Y;
            }
        }
        WW += size;
        Y += size;
        H -= size;
    }
    if (row_footer())
    {
        size = row_height(-2);
        if (ey>=Y+H-size-FUDGE && ey<=Y+H-size+FUDGE && inh)
        {
            if (row_resizable(-2))
            {
                drag_row = -2;
                anchor_top = ey;
                move |= MOVE_Y;
            }
        }
        H -= size;
    }
    if ( (move & MOVE_Y)==0 )
    {
        for (v=top_row();	v<rows();	WW+=size, v++ )
        {
            size = row_height(v);
            if (WW+size-FUDGE>=Y+H)
                break;
            if (ey>=WW+size-FUDGE && ey<=WW+size+FUDGE && inh)
            {
                if (row_resizable(v))
                {
                    drag_row = v;
                    anchor_top = WW;
                    move |= MOVE_Y;		//	Moving
                }
                break;
            }
        }
    }

    switch( move )
    {
    case MOVE_X:
        cursor = FL_CURSOR_WE;
        drag_row=-4;
        break;
    case MOVE_Y:
        cursor = FL_CURSOR_NS;
        drag_col=-4;
        break;
    case MOVE_XY:
        cursor = FL_CURSOR_NWSE;
        break;
    default:
        drag_row = drag_col = -4;
        cursor = FL_CURSOR_DEFAULT;
        break;
    }
    if (cursor!=last_cursor)
    {
        fl_cursor(cursor,FL_BLACK,FL_WHITE);
        last_cursor = cursor;
    }
}

bool Flv_Table::move_row( int amount )
{
    Flv_Style s;
    int r = row();

    if (!amount)
        return true;

    r += amount;
    if (r>=rows())
        r = rows()-1;
    if (r<0)
        r = 0;

    while(!select_locked())
    {
        get_style(s,r,col());
        if (!s.locked())
            break;
        r += (amount<0?-1:1);
        if ( r<0 || r>=rows() )
            return false;
    }
    if (r!=row())
    {
        row(r);
        return true;
    }
    return false;
}

bool Flv_Table::move_col( int amount )
{
    Flv_Style s;
    int c = col();

    if (!amount)
        return true;

    c += amount;
    if (c>=cols())
        c = cols()-1;
    if (c<0)
        c = 0;

    while(!select_locked())
    {
        get_style(s,row(),c);
        if (!s.locked())
            break;
        c += (amount<0?-1:1);
        if ( c<0 || c>=rows() )
            return false;
    }
    if (c!=col())
    {
        col(c);
        return true;
    }
    return false;
}


int Flv_Table::edit_when( int v )
{
    int wfocused = (Fl::focus()==veditor);
    if (v!=vedit_when)
    {
        vedit_when = v;
        if (vedit_when!=FLV_EDIT_ALWAYS)
            end_edit();
        else
            start_edit();
    }
    if (wfocused && !vediting)
    {
        Fl::focus(this);
//		take_focus();
        redraw();
    }
    return vedit_when;
}


void Flv_Table::start_edit(void)											//	Start editing
{
    if (!vediting)
    {
        vediting = true;
        switch_editor( row(), col() );
    }
}

void Flv_Table::end_edit(void)
{
    int wfocused = (Fl::focus()==veditor);
    if (veditor)
        switch_editor(-1,-1);
    if (wfocused && !vediting)
    {
        Fl::focus(this);
//		take_focus();
        redraw();
    }
}

void Flv_Table::cancel_edit(void)											//	Cancel editing
{
    int wfocused = (Fl::focus()==veditor);
    if (veditor)
    {
        veditor->hide();
        veditor->draw();
    }
    veditor = NULL;
    edit_row = -1;
    edit_col = -1;
    vediting = false;
//	switch_editor(-1, -1);
    if (wfocused && !vediting)
    {
        Fl::focus(this);
//		take_focus();
        redraw();
    }
}

void Flv_Table::switch_editor( int nr, int nc )
{
    Flv_Style s;
    int x, y, w, h, wfocused;
    //char buf[30];

    wfocused = (Fl::focus()==veditor);

    if (veditor)
    {
        if (edit_row>-1 && edit_col>-1)
            save_editor( veditor, edit_row, edit_col );
        edit_row=-1;
        edit_col=-1;
        veditor->hide();
        veditor->draw();
        veditor = NULL;
    }
    if (edit_when()==FLV_EDIT_ALWAYS)
    {
        vediting = true;
        if (nr<0)
            nr = row();
        if (nc<0)
            nc = col();
    }

    if (nr>-1 && nc>-1 && vediting)
    {
        get_style( s, nr, nc );
        if (s.editor_defined() && !s.locked())
        {
            veditor = s.editor();
            if (veditor)
            {
                edit_row = nr;
                edit_col = nc;
                veditor->hide();
                get_cell_bounds(x,y,w,h,nr,nc);
                position_editor(veditor, x,y,w,h, s);
                load_editor( veditor, nr, nc );
                veditor->show();
                Fl::focus(veditor);
//				veditor->take_focus();
                veditor->handle(FL_FOCUS);
                veditor->damage(FL_DAMAGE_ALL);
                veditor->draw();
            }
        }
    }
    if (!veditor)
    {
        vediting=false;
        edit_row=-1;
        edit_col=-1;
    }
    if (!veditor && wfocused)
    {
        Fl::focus(this);
//		take_focus();
        handle(FL_FOCUS);
    }
}

